home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / DDJ0192.ARJ / GRPHPROG.ASC < prev    next >
Text File  |  1991-11-21  |  20KB  |  471 lines

  1. _GRAPHIC PROGRAMMING COLUMN_
  2. by Michael Abrash
  3.  
  4. [LISTING ONE]
  5.  
  6.  
  7. ; Draws all pixels in the list of horizontal lines passed in, in
  8. ; mode X, the VGA's undocumented 320x240 256-color mode. Clips to
  9. ; the rectangle specified by (ClipMinX,ClipMinY),(ClipMaxX,ClipMaxY).
  10. ; Draws to the page specified by CurrentPageBase.
  11. ; C near-callable as:
  12. ;     void DrawHorizontalLineList(struct HLineList * HLineListPtr,
  13. ;          int Color);
  14. ;
  15. ; All assembly code tested with TASM 2.0 and MASM 5.0
  16.  
  17. SCREEN_WIDTH    equ     320
  18. SCREEN_SEGMENT  equ     0a000h
  19. SC_INDEX equ    03c4h   ;Sequence Controller Index
  20. MAP_MASK equ    2       ;Map Mask register index in SC
  21.  
  22. HLine   struc
  23. XStart  dw      ?       ;X coordinate of leftmost pixel in line
  24. XEnd    dw      ?       ;X coordinate of rightmost pixel in line
  25. HLine   ends
  26.  
  27. HLineList struc
  28. Lngth   dw      ?       ;# of horizontal lines
  29. YStart  dw      ?       ;Y coordinate of topmost line
  30. HLinePtr dw     ?       ;pointer to list of horz lines
  31. HLineList ends
  32.  
  33. Parms   struc
  34.                 dw      2 dup(?) ;return address & pushed BP
  35. HLineListPtr    dw      ?       ;pointer to HLineList structure
  36. Color           dw      ?       ;color with which to fill
  37. Parms   ends
  38.         .model small
  39.         .data
  40.         extrn   _CurrentPageBase:word,_ClipMinX:word
  41.         extrn   _ClipMinY:word,_ClipMaxX:word,_ClipMaxY:word
  42. ; Plane masks for clipping left and right edges of rectangle.
  43. LeftClipPlaneMask       db      00fh,00eh,00ch,008h
  44. RightClipPlaneMask      db      001h,003h,007h,00fh
  45.         .code
  46.         align   2
  47. ToFillDone:
  48.         jmp     FillDone
  49.         public _DrawHorizontalLineList
  50.         align   2
  51. _DrawHorizontalLineList proc
  52.         push    bp              ;preserve caller's stack frame
  53.         mov     bp,sp           ;point to our stack frame
  54.         push    si              ;preserve caller's register variables
  55.         push    di
  56.         cld                     ;make string instructions inc pointers
  57.         mov     dx,SC_INDEX
  58.         mov     al,MAP_MASK
  59.         out     dx,al           ;point SC Index to the Map Mask
  60.         mov     ax,SCREEN_SEGMENT
  61.         mov     es,ax   ;point ES to display memory for REP STOS
  62.         mov     si,[bp+HLineListPtr] ;point to the line list
  63.         mov     bx,[si+HLinePtr] ;point to the XStart/XEnd descriptor
  64.                                 ; for the first (top) horizontal line
  65.         mov     cx,[si+YStart]  ;first scan line to draw
  66.         mov     si,[si+Lngth]   ;# of scan lines to draw
  67.         cmp     si,0            ;are there any lines to draw?
  68.         jle     ToFillDone      ;no, so we're done
  69.         cmp     cx,[_ClipMinY]  ;clipped at top?
  70.         jge     MinYNotClipped  ;no
  71.         neg     cx              ;yes, discard however many lines are
  72.         add     cx,[_ClipMinY]  ; clipped
  73.         sub     si,cx           ;that many fewer lines to draw
  74.         jle     ToFillDone      ;no lines left to draw
  75.         shl     cx,1            ;lines to skip*2
  76.         shl     cx,1            ;lines to skip*4
  77.         add     bx,cx           ;advance through the line list
  78.         mov     cx,[_ClipMinY]  ;start at the top clip line
  79. MinYNotClipped:
  80.         mov     dx,si
  81.         add     dx,cx           ;bottom row to draw + 1
  82.         cmp     dx,[_ClipMaxY]  ;clipped at bottom?
  83.         jle     MaxYNotClipped  ;no
  84.         sub     dx,[_ClipMaxY]  ;# of lines to clip off the bottom
  85.         sub     si,dx           ;# of lines left to draw
  86.         jle     ToFillDone      ;all lines are clipped
  87. MaxYNotClipped:
  88.         mov     ax,SCREEN_WIDTH/4 ;point to the start of the first
  89.         mul     cx              ; scan line on which to draw
  90.         add     ax,[_CurrentPageBase] ;offset of first line
  91.         mov     dx,ax           ;ES:DX points to first scan line to
  92.                                 ; draw
  93.         mov     ah,byte ptr [bp+Color] ;color with which to fill
  94. FillLoop:
  95.         push    bx              ;remember line list location
  96.         push    dx              ;remember offset of start of line
  97.         push    si              ;remember # of lines to draw
  98.         mov     di,[bx+XStart]  ;left edge of fill on this line
  99.         cmp     di,[_ClipMinX]  ;clipped to left edge?
  100.         jge     MinXNotClipped  ;no
  101.         mov     di,[_ClipMinX]  ;yes, clip to the left edge
  102. MinXNotClipped:
  103.         mov     si,di
  104.         mov     cx,[bx+XEnd]    ;right edge of fill
  105.         cmp     cx,[_ClipMaxX]  ;clipped to right edge?
  106.         jl      MaxXNotClipped  ;no
  107.         mov     cx,[_ClipMaxX]  ;yes, clip to the right edge
  108.         dec     cx
  109. MaxXNotClipped:
  110.         cmp     cx,di
  111.         jl      LineFillDone    ;skip if negative width
  112.         shr     di,1    ;X/4 = offset of first rect pixel in scan
  113.         shr     di,1    ; line
  114.         add     di,dx   ;offset of first rect pixel in display mem
  115.         mov     dx,si   ;XStart
  116.         and     si,0003h                ;look up left edge plane mask
  117.         mov     bh,LeftClipPlaneMask[si] ; to clip & put in BH
  118.         mov     si,cx
  119.         and     si,0003h                  ;look up right edge plane
  120.         mov     bl,RightClipPlaneMask[si] ; mask to clip & put in BL
  121.         and     dx,not 011b     ;calculate # of addresses across rect
  122.         sub     cx,dx
  123.         shr     cx,1
  124.         shr     cx,1    ;# of addresses across rectangle to fill - 1
  125.         jnz     MasksSet ;there's more than one byte to draw
  126.         and     bh,bl   ;there's only one byte, so combine the left
  127.                         ; and right edge clip masks
  128. MasksSet:
  129.         mov     dx,SC_INDEX+1 ;already points to the Map Mask reg
  130. FillRowsLoop:
  131.         mov     al,bh   ;put left-edge clip mask in AL
  132.         out     dx,al   ;set the left-edge plane (clip) mask
  133.         mov     al,ah   ;put color in AL
  134.         stosb           ;draw the left edge
  135.         dec     cx      ;count off left edge byte
  136.         js      FillLoopBottom ;that's the only byte
  137.         jz      DoRightEdge ;there are only two bytes
  138.         mov     al,00fh ;middle addresses are drawn 4 pixels at a pop
  139.         out     dx,al   ;set the middle pixel mask to no clip
  140.         mov     al,ah   ;put color in AL
  141.         rep     stosb   ;draw the middle addresses four pixels apiece
  142. DoRightEdge:
  143.         mov     al,bl   ;put right-edge clip mask in AL
  144.         out     dx,al   ;set the right-edge plane (clip) mask
  145.         mov     al,ah   ;put color in AL
  146.         stosb           ;draw the right edge
  147. FillLoopBottom:
  148. LineFillDone:
  149.         pop     si              ;retrieve # of lines to draw
  150.         pop     dx              ;retrieve offset of start of line
  151.         pop     bx              ;retrieve line list location
  152.         add     dx,SCREEN_WIDTH/4 ;point to start of next line
  153.         add     bx,size HLine   ;point to the next line descriptor
  154.         dec     si              ;count down lines
  155.         jnz     FillLoop
  156. FillDone:
  157.         pop     di              ;restore caller's register variables
  158.         pop     si
  159.         pop     bp              ;restore caller's stack frame
  160.         ret
  161. _DrawHorizontalLineList endp
  162.         end
  163.  
  164.  
  165. [LISTING TWO]
  166.  
  167.  
  168. /* Matrix arithmetic functions.
  169.    Tested with Borland C++ 2.0 in the small model */
  170.  
  171. /* Matrix multiplies Xform by SourceVec, and stores the result in
  172.    DestVec. Multiplies a 4x4 matrix times a 4x1 matrix; the result
  173.    is a 4x1 matrix, as follows:
  174.    --     --     -- --     -- --
  175.    |       |     | 4 |     | 4 |
  176.    |  4x4  |  X  | x |  =  | x |
  177.    |       |     | 1 |     | 1 |
  178.    --     --     -- --     -- -- */
  179. void XformVec(double Xform[4][4], double * SourceVec,
  180.    double * DestVec)
  181. {
  182.    int i,j;
  183.  
  184.    for (i=0; i<4; i++) {
  185.       DestVec[i] = 0;
  186.       for (j=0; j<4; j++)
  187.          DestVec[i] += Xform[i][j] * SourceVec[j];
  188.    }
  189. }
  190.  
  191. /* Matrix multiplies SourceXform1 by SourceXform2 and stores the
  192.    result in DestXform. Multiplies a 4x4 matrix times a 4x4 matrix;
  193.    the result is a 4x4 matrix, as follows:
  194.    --     --     --     --     --     --
  195.    |       |     |       |     |       |
  196.    |  4x4  |  X  |  4x4  |  =  |  4x4  |
  197.    |       |     |       |     |       |
  198.    --     --     --     --     --     -- */
  199. void ConcatXforms(double SourceXform1[4][4], double SourceXform2[4][4],
  200.    double DestXform[4][4])
  201. {
  202.    int i,j,k;
  203.  
  204.    for (i=0; i<4; i++) {
  205.       for (j=0; j<4; j++) {
  206.          DestXform[i][j] = 0;
  207.          for (k=0; k<4; k++)
  208.             DestXform[i][j] += SourceXform1[i][k] * SourceXform2[k][j];
  209.       }
  210.    }
  211. }
  212.  
  213.  
  214. [LISTING THREE]
  215.  
  216.  
  217. /* Transforms convex polygon Poly (which has PolyLength vertices),
  218.    performing the transformation according to Xform (which generally
  219.    represents a transformation from object space through world space
  220.    to view space), then projects the transformed polygon onto the
  221.    screen and draws it in color Color. Also updates the extent of the
  222.    rectangle (EraseRect) that's used to erase the screen later.
  223.    Tested with Borland C++ 2.0 in the small model */
  224. #include "polygon.h"
  225.  
  226. void XformAndProjectPoly(double Xform[4][4], struct Point3 * Poly,
  227.    int PolyLength, int Color)
  228. {
  229.    int i;
  230.    struct Point3 XformedPoly[MAX_POLY_LENGTH];
  231.    struct Point ProjectedPoly[MAX_POLY_LENGTH];
  232.    struct PointListHeader Polygon;
  233.  
  234.    /* Transform to view space, then project to the screen */
  235.    for (i=0; i<PolyLength; i++) {
  236.       /* Transform to view space */
  237.       XformVec(Xform, (double *)&Poly[i], (double *)&XformedPoly[i]);
  238.       /* Project the X & Y coordinates to the screen, rounding to the
  239.          nearest integral coordinates. The Y coordinate is negated to
  240.          flip from view space, where increasing Y is up, to screen
  241.          space, where increasing Y is down. Add in half the screen
  242.          width and height to center on the screen */
  243.       ProjectedPoly[i].X = ((int) (XformedPoly[i].X/XformedPoly[i].Z *
  244.             PROJECTION_RATIO*(SCREEN_WIDTH/2.0)+0.5))+SCREEN_WIDTH/2;
  245.       ProjectedPoly[i].Y = ((int) (XformedPoly[i].Y/XformedPoly[i].Z *
  246.             -1.0 * PROJECTION_RATIO * (SCREEN_WIDTH / 2.0) + 0.5)) +
  247.             SCREEN_HEIGHT/2;
  248.       /* Appropriately adjust the extent of the rectangle used to
  249.          erase this page later */
  250.          if (ProjectedPoly[i].X > EraseRect[NonDisplayedPage].Right)
  251.           if (ProjectedPoly[i].X < SCREEN_WIDTH)
  252.             EraseRect[NonDisplayedPage].Right = ProjectedPoly[i].X;
  253.           else EraseRect[NonDisplayedPage].Right = SCREEN_WIDTH;
  254.          if (ProjectedPoly[i].Y > EraseRect[NonDisplayedPage].Bottom)
  255.           if (ProjectedPoly[i].Y < SCREEN_HEIGHT)
  256.             EraseRect[NonDisplayedPage].Bottom = ProjectedPoly[i].Y;
  257.           else EraseRect[NonDisplayedPage].Bottom = SCREEN_HEIGHT;
  258.          if (ProjectedPoly[i].X < EraseRect[NonDisplayedPage].Left)
  259.           if (ProjectedPoly[i].X > 0)
  260.             EraseRect[NonDisplayedPage].Left = ProjectedPoly[i].X;
  261.           else EraseRect[NonDisplayedPage].Left = 0;
  262.          if (ProjectedPoly[i].Y < EraseRect[NonDisplayedPage].Top)
  263.           if (ProjectedPoly[i].Y > 0)
  264.             EraseRect[NonDisplayedPage].Top = ProjectedPoly[i].Y;
  265.           else EraseRect[NonDisplayedPage].Top = 0;
  266.    }
  267.    /* Draw the polygon */
  268.    DRAW_POLYGON(ProjectedPoly, PolyLength, Color, 0, 0);
  269. }
  270.  
  271.  
  272. [LISTING FOUR]
  273.  
  274. /* POLYGON.H: Header file for polygon-filling code, also includes
  275.    a number of useful items for 3D animation. */
  276.  
  277. #define MAX_POLY_LENGTH 4  /* four vertices is the max per poly */
  278. #define SCREEN_WIDTH 320
  279. #define SCREEN_HEIGHT 240
  280. #define PAGE0_START_OFFSET 0
  281. #define PAGE1_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH)/4)
  282. /* Ratio: distance from viewpoint to projection plane / width of
  283.    projection plane. Defines the width of the field of view. Lower
  284.    absolute values = wider fields of view; higher values = narrower */
  285. #define PROJECTION_RATIO   -2.0 /* negative because visible Z
  286.                                    coordinates are negative */
  287. /* Draws the polygon described by the point list PointList in color
  288.    Color with all vertices offset by (X,Y) */
  289. #define DRAW_POLYGON(PointList,NumPoints,Color,X,Y)         \
  290.    Polygon.Length = NumPoints;                              \
  291.    Polygon.PointPtr = PointList;                            \
  292.    FillConvexPolygon(&Polygon, Color, X, Y);
  293.  
  294. /* Describes a single 2D point */
  295. struct Point {
  296.    int X;   /* X coordinate */
  297.    int Y;   /* Y coordinate */
  298. };
  299. /* Describes a single 3D point in homogeneous coordinates */
  300. struct Point3 {
  301.    double X;   /* X coordinate */
  302.    double Y;   /* Y coordinate */
  303.    double Z;   /* Z coordinate */
  304.    double W;
  305. };
  306. /* Describes a series of points (used to store a list of vertices that
  307.    describe a polygon; each vertex is assumed to connect to the two
  308.    adjacent vertices, and the last vertex is assumed to connect to the
  309.    first) */
  310. struct PointListHeader {
  311.    int Length;                /* # of points */
  312.    struct Point * PointPtr;   /* pointer to list of points */
  313. };
  314.  
  315. /* Describes the beginning and ending X coordinates of a single
  316.    horizontal line */
  317. struct HLine {
  318.    int XStart; /* X coordinate of leftmost pixel in line */
  319.    int XEnd;   /* X coordinate of rightmost pixel in line */
  320. };
  321.  
  322. /* Describes a Length-long series of horizontal lines, all assumed to
  323.    be on contiguous scan lines starting at YStart and proceeding
  324.    downward (used to describe a scan-converted polygon to the
  325.    low-level hardware-dependent drawing code) */
  326. struct HLineList {
  327.    int Length;                /* # of horizontal lines */
  328.    int YStart;                /* Y coordinate of topmost line */
  329.    struct HLine * HLinePtr;   /* pointer to list of horz lines */
  330. };
  331. struct Rect { int Left, Top, Right, Bottom; };
  332.  
  333. extern void XformVec(double Xform[4][4], double * SourceVec,
  334.    double * DestVec);
  335. extern void ConcatXforms(double SourceXform1[4][4],
  336.    double SourceXform2[4][4], double DestXform[4][4]);
  337. extern void XformAndProjectPoly(double Xform[4][4],
  338.    struct Point3 * Poly, int PolyLength, int Color);
  339. extern int FillConvexPolygon(struct PointListHeader *, int, int, int);
  340. extern void Set320x240Mode(void);
  341. extern void ShowPage(unsigned int StartOffset);
  342. extern void FillRectangleX(int StartX, int StartY, int EndX,
  343.    int EndY, unsigned int PageBase, int Color);
  344. extern int DisplayedPage, NonDisplayedPage;
  345. extern struct Rect EraseRect[];
  346.  
  347.  
  348. [LISTING FIVE]
  349.  
  350. /* Simple 3D drawing program to view a polygon as it rotates in
  351.    mode X. View space is congruent with world space, with the
  352.    viewpoint fixed at the origin (0,0,0) of world space, looking in
  353.    the direction of increasingly negative Z. A right-handed
  354.    coordinate system is used throughout.
  355.    Tested with Borland C++ 2.0 in the small model */
  356. #include <conio.h>
  357. #include <stdio.h>
  358. #include <dos.h>
  359. #include <math.h>
  360. #include "polygon.h"
  361. void main(void);
  362.  
  363. /* Base offset of page to which to draw */
  364. unsigned int CurrentPageBase = 0;
  365. /* Clip rectangle; clips to the screen */
  366. int ClipMinX=0, ClipMinY=0;
  367. int ClipMaxX=SCREEN_WIDTH, ClipMaxY=SCREEN_HEIGHT;
  368. /* Rectangle specifying extent to be erased in each page */
  369. struct Rect EraseRect[2] = { {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT},
  370.    {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT} };
  371. /* Transformation from polygon's object space to world space.
  372.    Initially set up to perform no rotation and to move the polygon
  373.    into world space -140 units away from the origin down the Z axis.
  374.    Given the viewing point, -140 down the Z axis means 140 units away
  375.    straight ahead in the direction of view. The program dynamically
  376.    changes the rotation and translation */
  377. static double PolyWorldXform[4][4] = {
  378.    {1.0, 0.0, 0.0, 0.0},
  379.    {0.0, 1.0, 0.0, 0.0},
  380.    {0.0, 0.0, 1.0, -140.0},
  381.    {0.0, 0.0, 0.0, 1.0} };
  382. /* Transformation from world space into view space. In this program,
  383.    the view point is fixed at the origin of world space, looking down
  384.    the Z axis in the direction of increasingly negative Z, so view
  385.    space is identical to world space; this is the identity matrix */
  386. static double WorldViewXform[4][4] = {
  387.    {1.0, 0.0, 0.0, 0.0},
  388.    {0.0, 1.0, 0.0, 0.0},
  389.    {0.0, 0.0, 1.0, 0.0},
  390.    {0.0, 0.0, 0.0, 1.0}
  391. };
  392. static unsigned int PageStartOffsets[2] =
  393.    {PAGE0_START_OFFSET,PAGE1_START_OFFSET};
  394. int DisplayedPage, NonDisplayedPage;
  395.  
  396. void main() {
  397.    int Done = 0;
  398.    double WorkingXform[4][4];
  399.    static struct Point3 TestPoly[] =
  400.          {{-30,-15,0,1},{0,15,0,1},{10,-5,0,1}};
  401. #define TEST_POLY_LENGTH   (sizeof(TestPoly)/sizeof(struct Point3))
  402.    double Rotation = M_PI / 60.0; /* initial rotation = 3 degrees */
  403.    union REGS regset;
  404.  
  405.    Set320x240Mode();
  406.    ShowPage(PageStartOffsets[DisplayedPage = 0]);
  407.    /* Keep rotating the polygon, drawing it to the undisplayed page,
  408.       and flipping the page to show it */
  409.    do {
  410.       CurrentPageBase =    /* select other page for drawing to */
  411.             PageStartOffsets[NonDisplayedPage = DisplayedPage ^ 1];
  412.       /* Modify the object space to world space transformation matrix
  413.          for the current rotation around the Y axis */
  414.       PolyWorldXform[0][0] = PolyWorldXform[2][2] = cos(Rotation);
  415.       PolyWorldXform[2][0] = -(PolyWorldXform[0][2] = sin(Rotation));
  416.       /* Concatenate the object-to-world and world-to-view
  417.          transformations to make a transformation matrix that will
  418.          convert vertices from object space to view space in a single
  419.          operation */
  420.       ConcatXforms(WorldViewXform, PolyWorldXform, WorkingXform);
  421.       /* Clear the portion of the non-displayed page that was drawn
  422.          to last time, then reset the erase extent */
  423.       FillRectangleX(EraseRect[NonDisplayedPage].Left,
  424.             EraseRect[NonDisplayedPage].Top,
  425.             EraseRect[NonDisplayedPage].Right,
  426.             EraseRect[NonDisplayedPage].Bottom, CurrentPageBase, 0);
  427.       EraseRect[NonDisplayedPage].Left =
  428.             EraseRect[NonDisplayedPage].Top = 0x7FFF;
  429.       EraseRect[NonDisplayedPage].Right =
  430.          EraseRect[NonDisplayedPage].Bottom = 0;
  431.       /* Transform the polygon, project it on the screen, draw it */
  432.       XformAndProjectPoly(WorkingXform, TestPoly, TEST_POLY_LENGTH,9);
  433.       /* Flip to display the page into which we just drew */
  434.       ShowPage(PageStartOffsets[DisplayedPage = NonDisplayedPage]);
  435.       /* Rotate 6 degrees farther around the Y axis */
  436.       if ((Rotation += (M_PI/30.0)) >= (M_PI*2)) Rotation -= M_PI*2;
  437.       if (kbhit()) {
  438.          switch (getch()) {
  439.             case 0x1B:     /* Esc to exit */
  440.                Done = 1; break;
  441.             case 'A': case 'a':      /* away (-Z) */
  442.                PolyWorldXform[2][3] -= 3.0; break;
  443.             case 'T':      /* towards (+Z). Don't allow to get too */
  444.             case 't':      /* close, so Z clipping isn't needed */
  445.                if (PolyWorldXform[2][3] < -40.0)
  446.                      PolyWorldXform[2][3] += 3.0; break;
  447.             case 0:     /* extended code */
  448.                switch (getch()) {
  449.                   case 0x4B:  /* left (-X) */
  450.                      PolyWorldXform[0][3] -= 3.0; break;
  451.                   case 0x4D:  /* right (+X) */
  452.                      PolyWorldXform[0][3] += 3.0; break;
  453.                   case 0x48:  /* up (+Y) */
  454.                      PolyWorldXform[1][3] += 3.0; break;
  455.                   case 0x50:  /* down (-Y) */
  456.                      PolyWorldXform[1][3] -= 3.0; break;
  457.                   default:
  458.                      break;
  459.                }
  460.                break;
  461.             default:       /* any other key to pause */
  462.                getch(); break;
  463.          }
  464.       }
  465.    } while (!Done);
  466.    /* Return to text mode and exit */
  467.    regset.x.ax = 0x0003;   /* AL = 3 selects 80x25 text mode */
  468.    int86(0x10, ®set, ®set);
  469. }
  470.  
  471.